Matplotlib Tutorial, Adding Legends and Annotations
Adding a Legend
If we look at the line graphs of our previous examples, we realize, that we have to look into our code to understand what kind of function is depicted. This information should be available in the diagram for convenience. Legends are used for this purpose. The word stems from Latin and meand "to be read". So it "has to be read" to understand the graph.
Before legends have been used in mathematical graphs, they have been used in maps. Legends - as they are found in maps - describe the pictorial language or symbology of the map. Legends are used in line graphs to explain the function or the values underlying the different lines of the graph.
We will demonstrate in the following simple example how we can place a legend on a graph. A legend contains one or more entries. Every entry consists of a key and a label.
The pyplot function
legend(*args, **kwargs)
places a legend on the axes.
All we have to do to create a legend for lines, which already exist on the axes, is to simply call the function "legend" with an iterable of strings, one for each legend item. For example:
The easiest way to create a line graph in Matplitlib.
# next line only needed if working with "ipython notebook": %matplotlib inline import numpy as np import matplotlib.pyplot as plt ax = plt.gca() ax.plot([1, 2, 3, 4]) ax.legend(['A simple line'])We can expect the following output, if we execute the Python code aboveWe can expect the following output from the code above:
<matplotlib.legend.Legend at 0x7fa12108bc18>
If we add a label to the plot function, the value will be used as the label in the legend command. The only argument the legend function will still need is the location argument "loc":
If we add a label to the plot function, the values will be used in the legend command:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 25, 1000) y1 = np.sin(x) y2 = np.cos(x) plt.plot(x, y1, '-b', label='sine') plt.plot(x, y2, '-r', label='cosine') plt.legend(loc='upper left') plt.ylim(-1.5, 2.0) plt.show()
import numpy as np import matplotlib.pyplot as plt X = np.linspace(0, 25, 1000) F1 = np.sin(0.5 * X) F2 = 3 * np.cos(0.8*X) plt.plot(X, F1, label="$sin(0.5 * x)$") plt.plot(X, F2, label="$3 sin(x)$") plt.legend(loc='upper right')Executing the above code will return the following result:
<matplotlib.legend.Legend at 0x7fa120f17898>
In many cases we don't know what the result may look like before you plot it. It could be for example, that the legend will overshadow an important part of the lines. If you don't know what the data may look like, it may be best to use 'best' as the argument for loc. Matplotlib will automatically try to find the best possible location for the legend:
import numpy as np import matplotlib.pyplot as plt X = np.linspace(0, 25, 1000) F1 = np.sin(0.5 * X) F2 = 3 * np.cos(0.8*X) plt.plot(X, F1, label="$sin(0.5 * x)$") plt.plot(X, F2, label="$3 sin(x)$") plt.legend(loc='best')Executing the above code returns the following output:
<matplotlib.legend.Legend at 0x7fa120db1cf8>
loc='best'
can work very well:
import numpy as np import matplotlib.pyplot as plt X = np.linspace(-2 * np.pi, 2 * np.pi, 70, endpoint=True) F1 = np.sin(0.5*X) F2 = -3 * np.cos(0.8*X) plt.xticks( [-6.28, -3.14, 3.14, 6.28], [r'$-2\pi$', r'$-\pi$', r'$+\pi$', r'$+2\pi$']) plt.yticks([-3, -1, 0, +1, 3]) plt.plot(X, F1, label="$sin(0.5x)$") plt.plot(X, F2, label="$-3 cos(0.8x)$") plt.legend(loc='best') plt.show()
import numpy as np import matplotlib.pyplot as plt X = np.linspace(-2 * np.pi, 2 * np.pi, 70, endpoint=True) F1 = np.sin(0.5*X) F2 = 3 * np.cos(0.8*X) plt.xticks( [-6.28, -3.14, 3.14, 6.28], [r'$-2\pi$', r'$-\pi$', r'$+\pi$', r'$+2\pi$']) plt.yticks([-3, -1, 0, +1, 3]) plt.plot(X, F1, label="$sin(0.5x)$") plt.plot(X, F2, label="$3 cos(0.8x)$") plt.legend(loc='best') plt.show()
Annotations
Of course, the sinus function has "boring" and interesting values. Let's assume that you are especially interested in the value of \(3 * sin(3 * pi/4)\).
import numpy as np print(3 * np.sin(3 * np.pi/4))Executing the Python above code will return the following:
2.12132034356
The numerical result doesn't look special, but if we do a symbolic calculation for the above expression we get \(\frac{3}{\sqrt{2}}\). Now we want to label this point on the graph. We can do this with the annotate function. We want to annotate our graph with this point.
import numpy as np import matplotlib.pyplot as plt X = np.linspace(-2 * np.pi, 3 * np.pi, 70, endpoint=True) F1 = np.sin(X) F2 = 3 * np.sin(X) ax = plt.gca() plt.xticks( [-6.28, -3.14, 3.14, 6.28], [r'$-2\pi$', r'$-\pi$', r'$+\pi$', r'$+2\pi$']) plt.yticks([-3, -1, 0, +1, 3]) x = 3 * np.pi / 4 plt.scatter([x,],[3 * np.sin(x),], 50, color ='blue') plt.annotate(r'$(3\sin(\frac{3\pi}{4}),\frac{3}{\sqrt{2}})$', xy=(x, 3 * np.sin(x)), xycoords='data', xytext=(+20, +20), textcoords='offset points', fontsize=16, arrowprops=dict(facecolor='blue')) plt.plot(X, F1, label="$sin(x)$") plt.plot(X, F2, label="$3 sin(x)$") plt.legend(loc='lower left') plt.show()
We have to provide some informations to the parameters of annotate, we have used in our previous example.
| Parameter | Meaning |
|---|---|
| xy | coordinates of the arrow tip |
| xytext | coordinates of the text location |
The xy and the xytext locations of our example are in data coordinates. There are other coordinate systems available we can choose. The coordinate system of xy and xytext can be specified string values assigned to xycoords and textcoords. The default value is 'data':
| String Value | Coordinate System |
|---|---|
| figure points | points from the lower left corner of the figure |
| figure pixels | pixels from the lower left corner of the figure |
| figure fraction | 0,0 is lower left of figure and 1,1 is upper right |
| axes points | points from lower left corner of axes |
| axes pixels | pixels from lower left corner of axes |
| axes fraction | 0,0 is lower left of axes and 1,1 is upper right |
| data | use the axes data coordinate system |
Additionally, we can also specify the properties of the arrow. To do so, we have to provide a dictionary of arrow properties to the parameter arrowprops:
| arrowprops key | description |
|---|---|
| width | the width of the arrow in points |
| frac | the fraction of the arrow length occupied by the head |
| headwidth | the width of the base of the arrow head in points |
| shrink | move the tip and base some percent away from the annotated point and text |
| **kwargs | any key for matplotlib.patches.Polygon, e.g., facecolor |
In the following example, we will change the appearance of the arrow of our previous example:
import numpy as np import matplotlib.pyplot as plt X = np.linspace(-2 * np.pi, 3 * np.pi, 70, endpoint=True) F1 = np.sin(X) F2 = 3 * np.sin(X) ax = plt.gca() plt.xticks( [-6.28, -3.14, 3.14, 6.28], [r'$-2\pi$', r'$-\pi$', r'$+\pi$', r'$+2\pi$']) plt.yticks([-3, -1, 0, +1, 3]) x = 3 * np.pi / 4 plt.scatter([x,],[3 * np.sin(x),], 50, color ='blue') plt.annotate(r'$(3\sin(\frac{3\pi}{4}),\frac{3}{\sqrt{2}})$', xy=(x, 3 * np.sin(x)), xycoords='data', xytext=(+20, +20), textcoords='offset points', fontsize=16, arrowprops=dict(facecolor='blue', headwidth=15, frac=0.3, width=2)) plt.plot(X, F1, label="$sin(x)$") plt.plot(X, F2, label="$3 sin(x)$") plt.legend(loc='lower left') plt.show()

